---
id: quickstart
title: Quickstart
---

import useBaseUrl from '@docusaurus/useBaseUrl'
import Mermaid from '@theme/Mermaid'

ORY Kratos has several moving parts and getting everything right from the
beginning can be challenging. This getting started guide will help you install
ORY Kratos and some additional dependencies so that you can see how it works.

Please be aware that this guide is not a replacement for studying the docs. You
must understand core concepts and APIs to use ORY Kratos productively. This is
merely a guide to get you set up with some examples.

<iframe
  width="560"
  height="315"
  src="https://www.youtube.com/embed/5t1Zr_zJc7E"
  frameborder="0"
  allowfullscreen
></iframe>

## Use Case: Application Login and Registration

This section gives you some context on what we want to achieve and what tools we
need for that. You will also learn about the network set up we picked for this
guide.

This quickstart guide operates on the assumption that we are writing a NodeJS
app called **SecureApp**. This app is using nothing fancy - some ExpressJS and a
bit of HTML Templating using Handlebars. We do want to use TypeScript, but only
because it's more readable - not because we're doing anything out of the
ordinary!

You could pick any technology here, of course. This works with Swift, ReactJS,
or Angular (client side) as well as with PHP, Ruby, Python, Java (server side) -
you name it! We picked NodeJS + TypeScript because we believe it is the easiest
to understand, and because JavaScript and NodeJS are universally understood and
easy to install.

We know that SecureApp will need to have some type of dashboard and that it
needs users. Therefore, we need:

- Login
- Logout
- Registration
- Profile management ("update first name", "update avatar", etc.)
- Credentials Management ("add a new recovery email", "change password", etc.)
- Account Recovery ("password reset")
- Two Factor Authentication with Google Authenticator
- "Sign in with Google" and "Sign in with GitHub"

and of course:

- A dashboard that shows "Hello {{ firstName }} {{ lastName }}, your birthday is
  on {{ birthday }}!" which is only visible when the user is signed in.

## Setup

As you might already know, ORY Kratos is API-only; it does not have a UI or HTML
templating engine. We will implement all the user-facing UIs like "dashboard",
"login", and "registration" in our NodeJS SecureApp!

To ensure that no one can access the dashboard without prior authentication
(login), we can use a small piece of code (here ExpressJS) to do that:

```js
// Import the ORY Kratos SDK. SDKs are available for all popular programming
// languages! We will add examples for other programming languages here soon.
import { AdminApi, PublicApi } from '@oryd/kratos-client'

const publicEndpoint = new PublicApi('https://public.ory-kratos')
const adminEndpoint = new AdminApi('https://admin.ory-kratos')

const needsLogin = (req, res, next) => {
  new publicEndpoint.whoami(req)
    .then(({ body }) => {
      req.user = { session: body }
      next()
    })
    .catch(() => {
      res.redirect('/login')
    })
}

// You can use `needsLogin` as a middleware for Express or any other web framework:
//   import express from 'express'
//   const app = express()
//
//   app.get("/dashboard", needsLogin, dashboard)
//
```

:::info

ORY Kratos is not just an API: it uses cookies, HTTP redirects, anti-CSRF tokens
and more so you don't have to.

:::

Because our SecureApp and ORY Kratos need to share cookies, in order for
anti-CSRF tokens and login sessions to work, we will set up a path which
forwards requests to ORY Kratos' Public API so that both SecureApp and ORY
Kratos have the same hostname.

If an HTTP request is made to

```
https://my-secureapp/.ory/kratos/public/self-service/browser/flows/login
```

we forward the request (like a proxy) to

```
https://public.ory-kratos/self-service/browser/flows/login
```

and pipe the response back to the initial HTTP Request:

```js
import express from 'express'
import request from 'request'
const app = express()

const pathPrefix = '/.ory/kratos/public'
app.use(pathPrefix + '/', (req, res) => {
  const url = 'https://public.ory-kratos' + req.url.replace(pathPrefix, '')

  // Uses the `request` library to forward the request to ORY Kratos
  req.pipe(request(url, { followRedirect: false })).pipe(res)
})

// ...
// app.get("/dashboard", needsLogin, dashboard)
// ...
```

ORY Kratos does not ship with an administrative user interface. You must
implement that yourself or choose the ORY Cloud offering (to be announced). In
this quickstart we will use ORY Kratos' CLI to interact with ORY Kratos' APIs.

The quickstart also comes with [MailSlurper](https://mailslurper.com), a mock
SMTP server the demo uses to show how email verification works.

### Clone ORY Kratos and Run it in Docker

To get this example working, you will need Git,
[Docker](https://docs.docker.com/get-docker/), and
[Docker Compose](https://docs.docker.com/compose/install/) installed on your
system. No other dependencies are required. Before you start, make sure that
Docker has enough disk space.

:::tip

This tutorial uses Docker-Compose volumes which have reported to run out of disk
space. Check the remaining disk space using `docker system df`. If the volumes
are above the 85% threshold,
[prune old Docker objects](https://docs.docker.com/config/pruning/) before you
start!

:::

:::tip

If you encounter build errors (e.g. network timeout), make sure that the network
is running correctly and run `make docker` again. If the problem persists, feel
free to [open an issue](https://github.com/ory/kratos/issues/new/choose).

:::

Let's clone ORY Kratos and run `docker-compose`:

```shell script
git clone https://github.com/ory/kratos.git
cd kratos
git checkout v0.3.0-alpha.1

docker pull oryd/kratos:latest-sqlite
docker pull oryd/kratos-selfservice-ui-node:latest
docker-compose -f quickstart.yml -f quickstart-standalone.yml up --build --force-recreate
```

This might take a minute or two. Once the output slows down and logs indicate a
healthy system you're ready to roll! A healthy system will show something along
the lines of (the order of messages might be reversed):

```
kratos_1 | time="2020-01-20T14:52:13Z" level=info msg="Starting the admin httpd on: 0.0.0.0:4434"
kratos_1 | time="2020-01-20T14:52:13Z" level=info msg="Starting the public httpd on: 0.0.0.0:4433"
```

:::note

There are two important factors to get a fully functional system:

- You need to make sure that ports `4455`, `4433`, `4434`, and `4436`&nbsp;
  [are free](https://serverfault.com/questions/309052/check-if-port-is-open-or-closed-on-a-linux-server).
- Make sure to always use `127.0.0.1` as the hostname; never use `localhost`!
  This is important because browsers treat these two as separate domains and
  will therefore have issues with setting and using cookies correctly.

:::

You might notice that no database is being used in this example. ORY Kratos
supports SQLite, PostgreSQL, MySQL, and CockroachDB as database backends. For
the quickstart, we're mounting a persistent volume to store the SQLite database
in.

Future guides will explain how to set up a production system.

### Network Architecture

This demo makes use of several services:

1. [ORY Kratos](https://github.com/ory/kratos)

- Public ("Browser") API (port 4433)
- Admin API (port 4434) - This is only made public so we can test via the CLI.

2. [SecureApp](http://github.com/ory/kratos-selfservice-ui-node)

- Public (port 4455) - an example application written in NodeJS that implements
  the login, registration, logout, dashboard, and other UIs.

3. [MailSlurper](https://github.com/mailslurper)

- Public (port 4436) - a development SMTP server which ORY Kratos will use to
  send emails.

:::note

ORY Kratos' Public ("Browser") and Admin APIs are exactly the same, except the
Public API will make use of extra browser-related security features. All
requests from a web browser should use ORY Kratos' Public API, while everything
else should use the Admin API.

:::

To better understand the application architecture, let's take a look at the
network configuration. This assumes that you have at least some understanding of
how Docker networks work:

<Mermaid
  chart={`
graph TD
subgraph hn[Host Network]
    B[Browser]
    B-->|Can access URLs via 127.0.0.1:4455|OKPHN
    B-->|Can access UI via 127.0.0.1:4436|SMTPUI
    OKPHN([SecureApp exposed at :4455])
    SMTPUI([MailSlurper UI exposed at :4436])
end
subgraph dn["Internal Docker Network (intranet)"]
    OKPHN-.->SA
    SMTPUI-.->SMTP
    SA-->|Proxies URLs /.ory/kratos/public/* to|OK
    SA-->|Talks to and validates login sessions using|OK
    OK-->|Sends mail via|SMTP
    OK[ORY Kratos]
    SA["SecureApp (ORY Kratos SelfService UI Node Example)"]
    SMTP["SMTP Server (MailSlurper)"]
end
`}
/>

In order to avoid common cross-domain issues with cookies, we're proxying
requests to ORY Kratos' Public API so that all requests come from the same
hostname.

## Perform Registration, Login, and Logout

Enough theory, it's time to get this thing going! Let's start by trying to open
the dashboard - **go to
[127.0.0.1:4455/dashboard](http://127.0.0.1:4455/dashboard)**.

You should notice that you're ending up at the login endpoint instead of the
dashboard:

<p>
  <img
    alt="Login screen of your secured app"
    src={useBaseUrl('img/docs/secureapp-login.png')}
  />
</p>

Looking at the network stack, you can see two redirects happening:

<p>
  <img
    alt="Network trace of your secured app"
    src={useBaseUrl('img/docs/secureapp-login-ntrace.png')}
  />
</p>

Here's a play-by-play of what happened:

1. SecureApp used the ORY Kratos JavaScript language client to
   [guard the `/dashboard`](https://github.com/ory/kratos-selfservice-ui-node/blob/v0.3.0-alpha.1/src/index.ts#L111)
   route. The ORY Kratos client checked the cookies from the request and saw you
   were not logged in.
2. The route guard redirected you from `/dashboard` to `/auth/login`. ORY
   Kratos' browser API requires a `request-id` in order to log you in. It looked
   for this ID in the URL as a query parameter but
   [couldn't find it](https://github.com/ory/kratos-selfservice-ui-node/blob/2f097b519644d2663e277398213aaa1103b74cf9/src/routes/auth.ts#L26),
   so it decided to generate one for you.
3. `/auth/login` redirected you to
   `/.ory/kratos/public/self-service/browser/flows/login`, which is one of ORY
   Kratos' APIs used for logging in browser-based applications. This route is
   just forwarded from SecureApp to ORY Kratos.
4. ORY Kratos performed some security checks, prepared form data, created a
   `csrf_token`, and redirected the browser to
   `/auth/login?request=<request-id>`.
5. SecureApp handled the `/auth/login` route, found the `request-id` in the URL
   query parameter, and used it to make an HTTP request to
   `http://kratos:4434/self-service/browser/flows/requests/login?request=<request-id>`.
   Notice the URI is `kratos:4434` because SecureApp is making a server-side
   HTTP request via Docker's private network to ORY Kratos' Admin API.
6. ORY Kratos responded with data which SecureApp used to render the HTML login
   form.

:::note

Your rendered login form should be a standard HTML `<form>`. AJAX requests will
not work!

:::

Because we exposed ORY Kratos' Admin API on port `4434` of our host, we can use
`curl` to see what ORY Kratos responds with. Try visiting
`http://127.0.0.1:4455/auth/login`, copying the generated `request-id`, and
making the request yourself:

```shell script
$ curl "http://127.0.0.1:4434/self-service/browser/flows/requests/login?request=<request-id>" | jq
{
  "id": "4392b0ef-647a-4cf8-8045-57917b98e406",
  "expires_at": "2020-05-15T21:23:41.3220299Z",
  "issued_at": "2020-05-15T21:13:41.3220454Z",
  "request_url": "http://kratos:4433/self-service/browser/flows/login",
  "methods": {
    "oidc": {
      "method": "oidc",
      "config": {
        "action": "http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/registration/strategies/oidc/auth/4392b0ef-647a-4cf8-8045-57917b98e406",
        "method": "POST",
        "fields": [
          {
            "name": "csrf_token",
            "type": "hidden",
            "required": true,
            "value": "L3SmxtBF71wd6QLOqIdHbhg9OTLRrxyXm1LVWRfPlUAdxwNKwpy9wPe/pKtJqnpqnAB8ZfZp5yJypVTk+7wRuA=="
          }
        ]
      }
    },
    "password": {
      "method": "password",
      "config": {
        "action": "http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/login/strategies/password?request=4392b0ef-647a-4cf8-8045-57917b98e406",
        "method": "POST",
        "fields": [
          {
            "name": "identifier",
            "type": "text",
            "required": true,
            "value": ""
          },
          {
            "name": "password",
            "type": "password",
            "required": true
          },
          {
            "name": "csrf_token",
            "type": "hidden",
            "required": true,
            "value": "L3SmxtBF71wd6QLOqIdHbhg9OTLRrxyXm1LVWRfPlUAdxwNKwpy9wPe/pKtJqnpqnAB8ZfZp5yJypVTk+7wRuA=="
          }
        ]
      }
    }
  },
  "forced": false
}
```

This flow also works with Single Page Apps (SPA) and frameworks like Angular or
ReactJS. For more details about the specific flows (login, registration, logout,
etc.), head over to the [concept](concepts/index.md) chapter.

Let's move on to the next flow - registration! Click on "Register new account",
which initiates a flow similar to the one we just used:

<p>
  <img
    alt="Registration screen of your secured app"
    src={useBaseUrl('img/docs/secureapp-registration.png')}
  />
</p>

The network trace should look familiar by now:

<p>
  <img
    alt="Registration with network trace screen of your secured app"
    src={useBaseUrl('img/docs/secureapp-registration-ntrace.png')}
  />
</p>

If we try to sign up using a password like `123456`, Krato's password policy
will complain:

<p>
  <img
    alt="Registration with network trace screen of your secured app"
    src={useBaseUrl('img/docs/secureapp-registration-pwpolicy.png')}
  />
</p>

The error message is coming directly from ORY Kratos' Admin API:

```shell script
$ curl "http://127.0.0.1:4434/self-service/browser/flows/requests/registration?request=<request-id>" | jq
{
  "id": "53e4b68a-5f85-4e41-997f-d8d1f4113f0d",
  "expires_at": "2020-05-15T21:48:38.5044374Z",
  "issued_at": "2020-05-15T21:38:38.5044557Z",
  "request_url": "http://kratos:4433/self-service/browser/flows/registration",
  "methods": {
    "oidc": {
      "method": "oidc",
      "config": {
        "action": "http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/registration/strategies/oidc/auth/53e4b68a-5f85-4e41-997f-d8d1f4113f0d",
        "method": "POST",
        "fields": [
          {
            "name": "csrf_token",
            "type": "hidden",
            "required": true,
            "value": "tZUvp+2dSThQr0uklkfgWPRtPiYEyZQlBItc2RcIYdSHJoor/0QbpLr57cF3at1ccFB7cSMPb5DtfN1k+3vlLA=="
          }
        ]
      }
    },
    "password": {
      "method": "password",
      "config": {
        "action": "http://127.0.0.1:4455/.ory/kratos/public/self-service/browser/flows/registration/strategies/password?request=53e4b68a-5f85-4e41-997f-d8d1f4113f0d",
        "method": "POST",
        "fields": [
          {
            "name": "csrf_token",
            "type": "hidden",
            "required": true,
            "value": "M0/Koo7AEdU6O/FqfFP5uPxTCSTeiCvI1Wms37ZKsyIB/G8unBlDSdBtVw+dfsS8eG5Mc/lO0H08ni1iWjk32g=="
          },
          {
            "name": "password",
            "type": "password",
            "required": true,
            "errors": [
              {
                "message": "the password does not fulfill the password policy because: the password has been found in at least 23547453 data breaches and must no longer be used."
              }
            ]
          },
          {
            "name": "traits.email",
            "type": "text",
            "required": false,
            "value": "hello@ory.sh"
          }
        ]
      }
    }
  }
}
```

Setting a password that doesn't violate these policies, we will be immediately
redirected to the dashboard:

<p>
  <img
    alt="SecureApp Dashboard"
    src={useBaseUrl('img/docs/secureapp-dashboard.png')}
  />
</p>

By clicking the "logout" icon in the top right, you will be redirected to the
login screen again where you will be able to use your credentials to log back in
again. Exciting!

### Understanding How Login and Registration Works

Head over to the [Self-Service Flows Chapter](self-service/flows/index.md) for
an in-depth explanation of how each individual flow works.

### Email Verification

As you've signed up, an email was sent to the email address you used. Because
the quickstart uses a fake SMTP server, the email did not arrive in your inbox.
You can retrieve the email however by opening the MailSlurper UI at
[127.0.0.1:4436](http://127.0.0.1:4436).

You should see something like this:

<p>
  <img
    alt="User Email Verification"
    src={useBaseUrl('img/docs/mailslurper-quickstart.png')}
  />
</p>

If not, hard refresh the tab or click on the home icon in the menu bar.

Next, click the verification link. You will end up at the dashboard, with a
verified email address (check the `verified` and `verified_at` field in the JSON
response):

<p>
  <img
    alt="SecureApp Dashboard"
    src={useBaseUrl('img/docs/secureapp-verified-dashboard.png')}
  />
</p>

To re-request the verification email, fill out the form at
[127.0.0.1:4455/verify](http://127.0.0.1:4455/verify).

#### Configuration

You can find all configuration files used for this quickstart guide in
[`./contrib/quickstart/kratos`](https://github.com/ory/kratos/tree/v0.3.0-alpha.1/contrib/quickstart/kratos/email-password)
,
[`./quickstart.yml`](https://github.com/ory/kratos/blob/v0.3.0-alpha.1/quickstart.yml),
and
[`./quickstart-standalone.yml`](https://github.com/ory/kratos/blob/v0.3.0-alpha.1/quickstart-standalone.yml).
To understand what each of those configuration files does, consult the other
chapters of this documentation.

:::note

To get a minimal version of ORY Kratos running, you need to set configuration
values for
[`identity.traits.default_schema_url`](https://github.com/ory/kratos/blob/v0.3.0-alpha.1/contrib/quickstart/kratos/email-password/.kratos.yml#L67)
and
[`DSN`](https://github.com/ory/kratos/blob/v0.3.0-alpha.1/quickstart.yml#L42).
You should also configure
[`urls.*_ui`](https://github.com/ory/kratos/blob/v0.3.0-alpha.1/contrib/quickstart/kratos/email-password/.kratos.yml#L40)
or else Kratos will use fallback URLs.

:::

In the future, this guide will support more use cases such as:

- GitHub to login and registration

## Cleaning Up Docker

To clean everything up, you need to bring down the Docker Compose environment
and remove all mounted volumes.

```shell script
docker-compose -f quickstart.yml down -v
docker-compose -f quickstart.yml rm -fsv
```
